package tinybase.sm;

import junit.framework.Assert;
import tinybase.basic.RC;
import tinybase.basic.RedBase;
import tinybase.basic.RedBase.AttrType;
import tinybase.basic.RedBase.CompOp;

public class Interp {
	public static final int E_OK = 0;
	public static final int E_INCOMPATIBLE = -1;
	public static final int E_TOOMANY = -2;
	public static final int E_NOLENGTH = -3;
	public static final int E_INVINTSIZE = -4;
	public static final int E_INVREALSIZE = -5;
	public static final int E_INVFORMATSTRING = -6;
	public static final int E_INVSTRLEN = -7;
	public static final int E_DUPLICATEATTR = -8;
	public static final int E_TOOLONG = -9;
	public static final int E_STRINGTOOLONG = -10;
	
	static int mk_attr_infos(Node list, int max, AttrInfo attrInfos[]) {
		   int i;
		   int len;
		   AttrType type;
		   Node attr;
		   int errval;

		   /* for each element of the list... */
		   for(i = 0; list != null; ++i, list = ((LIST)list.content).next) {

		      /* if the list is too long, then error */
		      if(i == max)
		         return E_TOOMANY;

		      attr = ((LIST)list.content).curr;

		      /* Make sure the attribute name isn't too long */
		      if(((ATTRTYPE)attr.content).attrname.length() > RedBase.MAXNAME)
		         return E_TOOLONG;

		      /* interpret the format string */
		      Object[] res = parse_format_string(((ATTRTYPE)attr.content).type);
		      type = (AttrType)res[0];
		      len = ((Integer)res[1]).intValue();

		      /* add it to the list */
		      attrInfos[i].pAttrName = ((ATTRTYPE)attr.content).attrname;
		      attrInfos[i].attrType = type;
		      attrInfos[i].attrLength = len;
		   }

		   return i;
	}
	
	/*
	 * parse_format_string: deciphers a format string of the form: xl
	 * where x is a type specification (one of `i' INTEGER, `r' REAL,
	 * `s' STRING, or `c' STRING (character)) and l is a length (l is
	 * optional for `i' and `r'), and stores the type in *type and the
	 * length in *len.
	 *
	 * Returns
	 *    E_OK on success
	 *    error code otherwise
	 */
	static Object[] parse_format_string(String format_string) {
		Assert.assertTrue(format_string != null);
		Assert.assertTrue(format_string.length() > 0);
		AttrType type = null;
		Integer length = null;
		char c = format_string.charAt(0);
		if (c == 'i') {
			type = AttrType.INT;
			length = new Integer(4);
		}
		else if (c == 'f' || c=='r') {
			type = AttrType.FLOAT;
			length = 4;
		}
		else if (c == 's' || c=='c') {
			Assert.assertTrue(format_string.length() > 1);
			length = Integer.parseInt(format_string.substring(1));
			Assert.assertTrue(length.intValue() < RedBase.MAXSTRINGLEN);
		} else {
			Assert.assertTrue(false);
		}
		return new Object[] {type, length};
	}
	
	static int mk_rel_attrs(Node list, int max, RelAttr relAttrs[]) {
		   int i;

		   /* For each element of the list... */
		   for(i = 0; list != null; ++i, list = ((LIST)list.content).next){
		      /* If the list is too long then error */
		      if(i == max)
		         return E_TOOMANY;

		      mk_rel_attr(((LIST)list.content).curr, relAttrs[i]);
		   }

		   return i;
	}
	
	/*
	 * mk_rel_attr: converts a single relation-attribute (<relation,
	 * attribute> pair) into a RelAttr
	 */
	static void mk_rel_attr(Node node, RelAttr relAttr) {
		relAttr.pRelName = ((RELATTR)node.content).relname;
		relAttr.pAttrName = ((RELATTR)node.content).attrname;
	}
	
	/*
	 * mk_relations: converts a list of relations into an array of relations
	 *
	 * Returns:
	 *    the lengh of the list on success ( >= 0 )
	 *    error code otherwise
	 */
	static int mk_relations(Node list, int max, String[] relations) {
		   int i;
		   Node current;

		   /* for each element of the list... */
		   for(i = 0; list != null; ++i, list = ((LIST)list.content).next){
		      /* If the list is too long then error */
		      if(i == max)
		         return E_TOOMANY;

		      current = ((LIST)list.content).curr;
		      relations[i] = ((RELATION)current.content).relname;
		   }

		   return i;	
	}
	
	static int mk_conditions(Node list, int max, Condition[] conditions) {
		   int i;
		   Node current;

		   /* for each element of the list... */
		   for(i = 0; list != null; ++i, list = ((LIST)list.content).next){
		      /* If the list is too long then error */
		      if(i == max)
		         return E_TOOMANY;

		      current = ((LIST)list.content).curr;
		      conditions[i].lhsAttr.pRelName = 
		         ((RELATTR)((CONDITION)current.content).lhsRelattr.content).relname;
		      conditions[i].lhsAttr.pAttrName =
		    	  ((RELATTR)((CONDITION)current.content).lhsRelattr.content).attrname;
		      conditions[i].op = ((CONDITION)current.content).op;
		      if (((CONDITION)current.content).rhsRelattr != null) {
		         conditions[i].bRhsIsAttr = true;
		         conditions[i].rhsAttr.pRelName =
		        	 ((RELATTR)((CONDITION)current.content).rhsRelattr.content).relname;
		         conditions[i].rhsAttr.pAttrName = 
			    	  ((RELATTR)((CONDITION)current.content).rhsRelattr.content).attrname;
		      }
		      else {
		         conditions[i].bRhsIsAttr = false;
		         mk_value(((CONDITION)current.content).rhsValue, conditions[i].rhsValue);
		      }
		   }

		   return i;
	}
	
	static int mk_values(Node list, int max, Value values[]) {
		   int i;

		   /* for each element of the list... */
		   for(i = 0; list != null; ++i, list = ((LIST)list.content).next){
		      /* If the list is too long then error */
		      if(i == max)
		         return E_TOOMANY;

		      mk_value(((LIST)list.content).curr, values[i]);
		   }

		   return i;
	}
	
	static void mk_value(Node node, Value value) {
		   value.attrType = ((VALUE)node.content).type;
		   value.pData = ((VALUE)node.content).val;
	}
	
	static void print_error(String errmsg, int errval) {
		   if(errmsg != null)
			      System.err.printf("%s: ", errmsg);
			   switch(errval){
			      case E_OK:
			         System.err.printf("no error\n");
			         break;
			      case E_INCOMPATIBLE:
			         System.err.printf("attributes must be from selected relation(s)\n");
			         break;
			      case E_TOOMANY:
			         System.err.printf("too many elements\n");
			         break;
			      case E_NOLENGTH:
			         System.err.printf("length must be specified for STRING attribute\n");
			         break;
			      case E_INVINTSIZE:
			         System.err.printf("invalid size for INTEGER attribute (should be %d)\n",
			               4);
			         break;
			      case E_INVREALSIZE:
			         System.err.printf("invalid size for REAL attribute (should be %d)\n",
			               Float.SIZE / 8);
			         break;
			      case E_INVFORMATSTRING:
			         System.err.printf("invalid format string\n");
			         break;
			      case E_INVSTRLEN:
			         System.err.printf("invalid length for string attribute\n");
			         break;
			      case E_DUPLICATEATTR:
			         System.err.printf("duplicated attribute name\n");
			         break;
			      case E_TOOLONG:
			         System.err.printf("relation name or attribute name too long\n");
			         break;
			      case E_STRINGTOOLONG:
			         System.err.printf("string attribute too long\n");
			         break;
			      default:
			         System.err.printf( "unrecognized errval: %d\n", errval);
			   }
	}
	
	static void echo_query(Node n) {
		 switch(n.kind){
	      case Node.N_CREATETABLE:            /* for CreateTable() */
	         System.out.printf("create table %s (", ((CREATETABLE)n.content).relname);
	         print_attrtypes(((CREATETABLE)n.content).attrlist);
	         System.out.printf(")");
	         System.out.printf(";\n");
	         break;
	      case Node.N_CREATEINDEX:            /* for CreateIndex() */
	         System.out.printf("create index %s(%s);\n", ((CREATEINDEX)n.content).relname,
	               ((CREATEINDEX)n.content).attrname);
	         break;
	      case Node.N_DROPINDEX:            /* for DropIndex() */
	         System.out.printf("drop index %s(%s);\n", ((DROPINDEX)n.content).relname,
	        		 ((DROPINDEX)n.content).attrname);
	         break;
	      case Node.N_DROPTABLE:            /* for DropTable() */
	         System.out.printf("drop table %s;\n", ((DROPTABLE)n.content).relname);
	         break;
	      case Node.N_LOAD:            /* for Load() */
	         System.out.printf("load %s(\"%s\");\n",
	               ((LOAD)n.content).relname, ((LOAD)n.content).filename);
	         break;
	      case Node.N_HELP:            /* for Help() */
	         System.out.printf("help");
	         if(((HELP)n.content).relname != null)
	            System.out.printf(" %s", ((HELP)n.content).relname);
	         System.out.printf(";\n");
	         break;
	      case Node.N_PRINT:            /* for Print() */
	         System.out.printf("print %s;\n", ((PRINT)n.content).relname);
	         break;
	      case Node.N_SET:                                 /* for Set() */
	         System.out.printf("set %s = \"%s\";\n", ((SET)n.content).paramName, ((SET)n.content).string);
	         break;
	      case Node.N_QUERY:            /* for Query() */
	         System.out.printf("select ");
	         print_relattrs(((QUERY)n.content).relattrlist);
	         System.out.printf("\n from ");
	         print_relations(((QUERY)n.content).rellist);
	         System.out.printf("\n");
	         if (((QUERY)n.content).conditionlist != null) {
	            System.out.printf("where ");
	            print_conditions(((QUERY)n.content).conditionlist);
	         }
	         System.out.printf(";\n");
	         break;
	      case Node.N_INSERT:            /* for Insert() */
	         System.out.printf("insert into %s values ( ", ((INSERT)n.content).relname);
	         print_values(((INSERT)n.content).valuelist);
	         System.out.printf(");\n");
	         break;
	      case Node.N_DELETE:            /* for Delete() */
	         System.out.printf("delete %s ", ((DELETE)n.content).relname);
	         if (((DELETE)n.content).conditionlist != null) {
	            System.out.printf("where ");
	            print_conditions(((DELETE)n.content).conditionlist);
	         }
	         System.out.printf(";\n");
	         break;
	      case Node.N_UPDATE:            /* for Update() */
	         {
	            System.out.printf("update %s set ",((UPDATE)n.content).relname);
	            print_relattr(((UPDATE)n.content).relattr);
	            System.out.printf(" = ");
	            Node rhs = ((UPDATE)n.content).relorvalue;

	            /* The RHS can be either a relation.attribute or a value */
	            if (((RELATTR_OR_VALUE)rhs.content).relattr != null) {
	               /* Print out the relation.attribute */
	               print_relattr(((RELATTR_OR_VALUE)rhs.content).relattr );
	            } else {
	               /* Print out the value */
	               print_value(((RELATTR_OR_VALUE)rhs.content).value);
	            }
	            if (((UPDATE)n.content).conditionlist != null) {
	               System.out.printf("where ");
	               print_conditions(((UPDATE)n.content).conditionlist);
	            }
	            System.out.printf(";\n");
	            break;
	         }
	      default:   // should never get here
	         break;
	   }
	   System.out.flush();
	}
	
	static void print_attrtypes(Node n) {
		Node attr;
		 for(; n != null; n = ((LIST)n.content).next){
		      attr = ((LIST)n.content).curr;
		      System.out.printf("%s = %s", ((ATTRTYPE)attr.content).attrname,  ((ATTRTYPE)attr.content).type);
		      if(((LIST)n.content).next != null)
		         System.out.printf(", ");
		   }
	}
	
	static void print_op(CompOp op) {
		switch(op){
	      case EQ_OP:
	         System.out.printf(" =");
	         break;
	      case NE_OP:
	         System.out.printf(" <>");
	         break;
	      case LT_OP:
	         System.out.printf(" <");
	         break;
	      case LE_OP:
	         System.out.printf(" <=");
	         break;
	      case GT_OP:
	         System.out.printf(" >");
	         break;
	      case GE_OP:
	         System.out.printf(" >=");
	         break;
	      case NO_OP:
	         System.out.printf(" NO_OP");
	         break;
	   }
	}
	
	static void print_relattr(Node n) {
		 System.out.printf(" ");
		   if (((RELATTR)n.content).relname != null)
		      System.out.printf("%0s.",((RELATTR)n.content).relname);
		   System.out.printf("%s",((RELATTR)n.content).attrname);
	}
	
	static void print_value(Node n) {
		switch(((VALUE)n.content).type){
	      case INT:
	         System.out.printf(" %d", ((VALUE)n.content).val);
	         break;
	      case FLOAT:
	         System.out.printf(" %f", ((VALUE)n.content).val);
	         break;
	      case STRING:
	         System.out.printf(" \"%s\"", ((VALUE)n.content).val);
	         break;
	   }
	}
	static void print_condition(Node n) {
		 print_relattr(((CONDITION)n.content).lhsRelattr);
		   print_op(((CONDITION)n.content).op);
		   if (((CONDITION)n.content).rhsRelattr != null)
		      print_relattr(((CONDITION)n.content).rhsRelattr);
		   else
		      print_value(((CONDITION)n.content).rhsValue);
	}
	
	static void print_relattrs(Node n) {
		   for(; n != null; n = ((LIST)n.content).next){
			   	print_relattr(((LIST)n.content).curr);
			      if(((LIST)n.content).next != null)
			         System.out.printf(",");
			   }
	}
	static void print_relations(Node n) {
		   for(; n != null; n = ((LIST)n.content).next){
			   System.out.printf(" %s", ((RELATION)((LIST)n.content).curr.content).relname);
			      if(((LIST)n.content).next != null)
				         System.out.printf(",");
				   }
	}
	
	static void print_conditions(Node n) {
		   for(; n != null; n = ((LIST)n.content).next){
			      print_condition(((LIST)n.content).curr);
			      if(((LIST)n.content).next != null)
			         System.out.printf(" and");
			   }
	}
	
	static void print_values(Node n) {
		   for(; n != null; n = ((LIST)n.content).next){
			      print_value(((LIST)n.content).curr);
			      if(((LIST)n.content).next != null)
			         System.out.printf(",");
			   }
	}
	

}
